how to join and map using prisma client
- aliases
- No value
- tags
- No value
- description
- No value
- status
- μμ±μλ£
- project
- false
- area
- true
- resource
- false
- title
- how to join and map using prisma client
- created
- 2025-02-20T22:02:08
- updated
- 2025-04-12T16:13:23
My case
AcademyCoachμ User μ¬μ΄μ 릴λ μ΄μ
μ μ¬μ©νλ, userModelμ΄λΌλ κΈ°λ³ΈμΌλ‘ μ§μ λ νλκ° μλ userInfoλΌλ μμμ νλμ User μν°ν°λ₯Ό λ§€ννκΈ° μν΄ μ¬μ©νλ€.
async listAcademyCoach(
academyId: string,
): Promise<AcademyCoachDtoResponse[]> {
return await this.prisma.academyCoach
.findMany({
include: {
userModel: true, // JOIN and select related user entity into userModel
},
where: {
academy: academyId,
},
})
.then((dtos) =>
dtos.map((dto): AcademyCoachDtoResponse => {
const { userModel, ...rest } = dto;
return {
...rest,
userInfo: UserDto.from(userModel), // β λ°λ‘ μ¬κΈ°μμ λ§€νμ΄ μ΄λ£¨μ΄μ§λ€.
};
}),
);
}
μλλ Prisma Schema
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
createdTime DateTime @default(now()) @db.Date
updatedTime DateTime @updatedAt @db.Date
deletedTime DateTime? @db.Date
AcademyCoach AcademyCoach[]
tickets Ticket[]
@@map("usermodels")
}
model AcademyCoach {
id String @id @default(auto()) @map("_id") @db.ObjectId
academy String @db.ObjectId
user String @db.ObjectId
createdTime DateTime @default(now()) @db.Date
updatedTime DateTime @updatedAt @db.Date
deletedTime DateTime? @db.Date
userModel User @relation(fields: [user], references: [id])
academyModel Academy @relation(fields: [academy], references: [id])
@@map("academycoachmodels")
}
GPT μ€μ
leftJoinAndMapOne can achieve my case.Yes, Prisma does not have a direct equivalent to TypeORM's leftJoinAndMapOne, but you can achieve similar functionality using relations and computed fields.
Solution 1: Using include or select
If the relation is defined in your schema, you can use include or select to fetch the related entity.
Example:
model User {
id Int @id @default(autoincrement())
name String
profile Profile? @relation(fields: [profileId], references: [id])
profileId Int?
}
model Profile {
id Int @id @default(autoincrement())
bio String
user User?
}
To fetch User with the Profile included:
const users = await prisma.user.findMany({
include: {
profile: true,
},
});
Solution 2: Using computed properties via a Service Layer
Since Prisma doesn't allow mapping a joined property directly like TypeORM's leftJoinAndMapOne, you can manually add the joined property in your service layer.
const usersWithMappedProfile = await prisma.user.findMany({
include: {
profile: true,
},
});
const result = usersWithMappedProfile.map(user => ({
...user,
profileBio: user.profile?.bio || null,
}));
This effectively adds a profileBio field like leftJoinAndMapOne does in TypeORM.
Solution 3: Raw SQL with prisma.$queryRaw
If you need a more complex join:
const usersWithProfile = await prisma.$queryRaw`
SELECT u.id, u.name, p.bio AS profileBio
FROM User u
LEFT JOIN Profile p ON u.profileId = p.id;
`;
But this approach does not return a typed model like Prisma usually does.
Conclusion
While Prisma does not have an exact equivalent of leftJoinAndMapOne, the combination of:
include/selectfor relations- Transforming the result in your service layer
- Using raw SQL queries when necessary
achieves the same effect. The most idiomatic Prisma approach is to use include and process the mapping at the service layer.
2025-04-12 νμ¬λ Dto ν©ν 리 λ©μλμ λ§€ν λ‘μ§μ λ΄μλκ³ μμ.
export class TicketOrderDto extends DtoTransform {
static from(
input: TicketOrder & {
userModel?: User;
ticketModel?: Ticket & {
ticketPlanModel?: TicketPlan;
};
},
): TicketOrderDto {
const dto = this.transform<typeof input, TicketOrderDto>(input);
// μ°κ΄ νλ λ§€ν
dto.ticket = input.ticketModel
? TicketWithReservationTimeDto.from(input.ticketModel)
: undefined;
dto.userDto = input.userModel ? UserDto.from(input.userModel) : undefined;
dto.ticketPlanDto = input.ticketModel?.ticketPlanModel
? TicketPlanDto.from(input.ticketModel.ticketPlanModel)
: undefined;
return dto;
}
...
@NestedProperty({ type: TicketPlanDto, optional: true })
ticketPlanDto?: TicketPlanDto;
@NestedProperty({ type: TicketWithReservationTimeDto, optional: true })
ticket?: TicketWithReservationTimeDto;
@NestedProperty({ type: UserDto, optional: true })
userDto?: UserDto;
}